require 'exifr'
#Main class for the Item as described in UPnP ContentDirectory 1.0

class UPnP::Service::NObject
  attr_reader :title, :childCount
  attr_accessor :id, :parentId, :file 
#  Property   Namespace Required  Multiple 
#                                   Values
#  id           yes     no 
#  parentID     yes     no 
#  title        yes     no 
#  creator      no      no 
#  res          no      yes 
#  class        yes     no 
#  restricted   yes     no 
#  writeStatus  no      no 
#  refID        no      no
  
  def initialize(title)
    @class = 'object'
    @title = title
    @restricted = true
  end
  
  def <=>(other)
    return @title <=> other.title
  end
  
  def id
    if not instance_variable_defined?(:@id)
      raise IndexError.new("Object '#@title' has no ID yet defined")
    end
    @id
  end

  def to_xml(x)
    x.dc  :title, @title
    x.upnp :class, @class
    yield self if Kernel.block_given?
  end
end

class UPnP::Service::Container < UPnP::Service::NObject
  include Enumerable
  attr_reader :childCount

  #each containter tree has @object instance method that points to the array, containing full tree
  #from the very top root object to the very last one

#  Property  Required  Multiple 
#                       Values
#  childCount   no      no
#  createClass  no      yes
#  searchClass  no      yes
#  searchable   no      no

  def self.root_container
    UPnP::Service::Container.new('root') do
      |c| c.id = 0
      c.parentId = -1
      c.objects = [] << c
    end
  end

  def initialize(title)
    super
    @class << '.container'
    yield self if Kernel.block_given?
    
    @childCount = 0
    
    self
  end

  def [](i)
    @objects[i]
  end

  #add new object to the list
  def <<(object)

    object.objects = @objects if object.kind_of? UPnP::Service::Container 
    object.parentId = self.id
    object.id = @objects.size
    @objects << object
    @childCount += 1
    
    self
  end
 
  #Enumerable Each method
  def each
    @objects.select {|o| o.parentId == @id}.each do |a|
      yield a
    end
  end
  
  #Correct sort order: Containers first, items later, all in alphabetical
  def <=>(other)
    if other.kind_of? UPnP::Service::Container
      return super
    end
    
    #containers go first and all the rest after
    return -1  
  end

  def to_xml(x)
    x.container(:id => @id, :parentId => @parentId, :childCount => @childCount, :restricted => @restricted)  do
      super
#      x.upnp :storageUsed, 'zzz'
      x.upnp :writeStatus, 'NOT_WRITABLE'
    end
  end
  
  def objects=(objects)
    @objects = objects
  end
  
end

class UPnP::Service::StorageFolder < UPnP::Service::Container
  attr_writer :storageUsed
#  Property   NameSpace Required 
#  
#  storageUsed  upnp      Yes
   
  def initialize(title)
    super
    @class << '.storageFolder'
  end 

  def to_xml(x)
    super do
      #set 'unknown' size by default if used storage is not defined
      x.upnp :storageUsed, @storageUsed || -1
    end
  end
end

class UPnP::Service::Item < UPnP::Service::NObject
#  Property  Required  Multiple 
#                       Values
#  refID        no      no

  def initialize(title)
    super
    
    @class << '.item'
  end
  
  def res(attributes, url)
    @res_attributes = attributes
    @res_url = url
  end
  #Correct sort order: Items go last, all in alphabetical
  def <=>(other)
    if other.kind_of? UPnP::Service::Item
      return super
    end
    
    #Items go last in order
    return 1  
  end

  def to_xml(x)
    _, port, host, addr = Thread.current[:WEBrickSocket].addr

    x.item(:id => @id, :parentId => @parentId, :restricted => @restricted)  do
      super
      x.dc :date, @date if instance_variable_defined? :@date
      
      x.tag! 'res', @res_attributes, @res_url.gsub(/\$\$server_host_port\$\$/, "#{addr}:#{port}") if instance_variable_defined? :@res_url
    end
  end
  
end

class UPnP::Service::VideoItem < UPnP::Service::Item
#  Property  NameSpace Required 
#  
#  genre            upnp  No 
#  longDescription  upnp  No 
#  producer         upnp  No 
#  rating           upnp  No 
#  actor            upnp  No 
#  director         upnp  No 
#  description      dc    No 
#  publisher        dc    No 
#  language         dc    No 
#  relation         dc    No 
  
  def initialize(title)
    super
    
    @class << '.videoItem'
  end
end

class UPnP::Service::Movie < UPnP::Service::VideoItem
#  Property       NameSpace Required 
#
#  storageMedium        upnp  No
#  DVDRegionCode        upnp  No
#  channelName          upnp  No
#  scheduledStartTime   upnp  No
#  scheduledEndTime     upnp  No

  def initialize(title)
    super
    
    @class << '.movie'
  end
end

class UPnP::Service::VideoBroadcast < UPnP::Service::VideoItem
  attr_writer :icon, :channelNr
#  alias :super_to_xml :to_xml
#  Property       NameSpace Required 
#
#  icon             upnp      No
#  region           upnp      No
#  channelNr        upnp      No

  def initialize(title)
    super
    @class << '.videoBroadcast'
  end

  def to_xml(xml)
    _, port, host, addr = Thread.current[:WEBrickSocket].addr

    super do
      xml.upnp :icon, @icon.gsub(/\$\$server_host_port\$\$/, "#{addr}:#{port}") if instance_variable_defined? :@icon
      xml.upnp :channelNr, @channelNr if @channelNr
    end
  end
end

class UPnP::Service::ImageItem < UPnP::Service::Item
#  Property     NameSpace Required 
#  
# longDescription   upnp    No
# storageMedium     upnp    No
# rating            upnp    No
# description       dc      No
# publisher         dc      No
# date              dc      No
# rights            dc      No

  def initialize(title)
    super
    @class << '.imageItem'
  end
end

class UPnP::Service::Photo < UPnP::Service::ImageItem
#  Property     NameSpace Required 
#  
# album             upnp    No
  def initialize(title)
    super
    @class << '.photo'
  end
  
  #read file date on the file setup 
  def file=(f)
    super
    fd = EXIFR::JPEG.new(f).date_time
    @date = fd.iso8601[0..9] if fd
  end
end

class UPnP::Service::TextItem < UPnP::Service::Item
#  Property     NameSpace Required 
#  
# author          upnp      No
# protection      upnp      No
# longDescription upnp      No
# storageMedium   upnp      No
# rating          upnp      No
# description     dc        No
# publisher       dc        No
# contributor     dc        No
# date            dc        No
# relation        dc        No
# language        dc        No
# rights          dc        No
  def initialize(title)
    super
    @class << '.textItem'
  end
end
